package info.sean.util.excel;


import org.apache.poi.ss.usermodel.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.*;

public class POIUtils {

    private final static String DATE_FORMAT = "yyyy-MM-dd";

    public static List  build(InputStream in, Class clazz) {
        List result = new ArrayList<>();
        try {
            Workbook workbook = WorkbookFactory.create(in);
            Sheet sheet = workbook.getSheetAt(0);
            HashMap<String, Integer> titles = new HashMap<>();
            //判断类上面是否有注解
            if (clazz.isAnnotationPresent(ExcelColumn.class)) {
                ExcelColumn classExcelColumn = (ExcelColumn) clazz.getAnnotation(ExcelColumn.class);
                int titleRow = classExcelColumn.titleRow();
                int rowStart = classExcelColumn.rowStart();
                short lastNum = sheet.getRow(titleRow).getLastCellNum();
                //title哈希表
                for (int i = 0; i < lastNum; i++) {
                    String title = (String) readRow(sheet.getRow(titleRow), String.class, i);
                    titles.put(title, i);
                }

                Field[] fields = clazz.getDeclaredFields();
                int lastRowNum = sheet.getLastRowNum();
                for (int i = rowStart; i <= lastRowNum; i++) {
                    Row currentRow = sheet.getRow(i);
                    Object instance = clazz.newInstance();
                    result.add(instance);
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(ExcelColumn.class)) {
                            ExcelColumn excelColumn = field.getAnnotation(ExcelColumn.class);
                            String value = excelColumn.value();
                            Object arg = readRow(currentRow, field.getType(), titles.get(value));
                            Method method = getMethod(clazz, field.getName());
                            if(Objects.nonNull(method))
                                method.invoke(instance, arg);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }


    private static Method getMethod(Class clazz, String fieldName) {
        final String PRE = "set";
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (method.getName().equalsIgnoreCase(PRE + fieldName)) {
                return method;
            }
        }
        return null;
    }

    private static Object readRow(Row row, Class clazz, int index) {
        if (Objects.isNull(row)) return null;
        Cell cell = row.getCell(index);
        if (Objects.isNull(cell)) return null;

        if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC && DateUtil.isCellDateFormatted(cell)) {
            Date theDate = cell.getDateCellValue();
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
            return convertType(clazz, sdf.format(theDate));
        }
        cell.setCellType(Cell.CELL_TYPE_STRING);
        String value = cell.getStringCellValue();
        return convertType(clazz, value);
    }

    /**
     * 类型转换
     */
    private static Object convertType(Class clazz, String value) {

        if (Integer.class == clazz || int.class == clazz) {
            return Integer.valueOf(value);
        }
        if (Short.class == clazz || short.class == clazz) {
            return Short.valueOf(value);
        }
        if (Byte.class == clazz || byte.class == clazz) {
            return Byte.valueOf(value);
        }
        if (Character.class == clazz || char.class == clazz) {
            return value.charAt(0);
        }
        if (Long.class == clazz || long.class == clazz) {
            return Long.valueOf(value);
        }
        if (Float.class == clazz || float.class == clazz) {
            return Float.valueOf(value);
        }
        if (Double.class == clazz || double.class == clazz) {
            return Double.valueOf(value);
        }
        if (Boolean.class == clazz || boolean.class == clazz) {
            return Boolean.valueOf(value.toLowerCase());
        }
        if (BigDecimal.class == clazz) {
            return new BigDecimal(value);
        }
        if (Date.class == clazz) {
            SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
            ParsePosition pos = new ParsePosition(0);
            Date date = formatter.parse(value, pos);
            return date;
        }
        return value;
    }
}
